package com.atguigu.tingshu.common.login;

import com.atguigu.tingshu.common.constant.RedisConstant;
import com.atguigu.tingshu.common.execption.GuiguException;
import com.atguigu.tingshu.common.result.ResultCodeEnum;
import com.atguigu.tingshu.common.util.AuthContextHolder;
import com.atguigu.tingshu.model.user.UserInfo;
import com.atguigu.tingshu.vo.user.UserInfoVo;
import jakarta.servlet.http.HttpServletRequest;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

/**
 * @version 1.0
 * @Author qXing
 * @Date 2025/9/1 16:41
 * @注释
 */
@Aspect
@Component
public class TsLoginAspect {
    @Autowired
    private RedisTemplate redisTemplate;


    //ProceedingJoinPoint 是 Spring AOP 中 JoinPoint 的一个子接口，它专门用于 @Around 通知
    //它封装了被代理的目标方法的所有信息，并且最关键的是，它提供了一个 proceed() 方法，用于决定是否以及何时继续执行目标方法。
    @Around("@annotation(tsLogin)")
    public Object doBasicProfiling(ProceedingJoinPoint point, TsLogin tsLogin) throws Throwable {
/*  判断当前用户是否登录，登录成功需要将token放入请求头中，判断请求头是否有token，有则返回用户信息，无则返回未登录
   有token则从redis中获取用户信息，无则返回未登录
* */
        //从当前线程的上下文中，获取到当前HTTP请求的属性容器。
        // 在当前线程（ThreadLocal）中持有与当前请求相关的属性（RequestAttributes）。
        RequestAttributes previousAttributes = RequestContextHolder.getRequestAttributes();
       //将通用的 RequestAttributes 接口转换为其在 Web 环境下的具体实现类，以便访问 Servlet 特有的方法。
        ServletRequestAttributes servletRequestAttributes = (ServletRequestAttributes) previousAttributes;
        //获取当前线程绑定的 HttpServletRequest 对象。
        HttpServletRequest request = servletRequestAttributes.getRequest();
        //获取当前线程绑定的 HttpServletResponse 响应对象。
        //  HttpServletResponse response = servletRequestAttributes.getResponse();
        //获取请求头中的token
        String token = request.getHeader("token");

        //判断注解的属性，required为true时，需要登录，默认为true
        if(tsLogin.required()) {
            //判断token是否为空
            if (StringUtils.isEmpty(token)) {
                throw new GuiguException(ResultCodeEnum.LOGIN_AUTH);
            }
            //判断redis中是否有token
            String userLoginKey = RedisConstant.USER_LOGIN_KEY_PREFIX + token;
            //获取缓存中的数据
            UserInfo userInfo = (UserInfo) this.redisTemplate.opsForValue().get(userLoginKey);
            //如果用户信息为空
            if (null == userInfo) {
                //
                throw new GuiguException(ResultCodeEnum.LOGIN_AUTH);
            }
        }
            //token不为空
            if(!StringUtils.isEmpty(token)){
                //有token
                String userLoginKey= RedisConstant.USER_LOGIN_KEY_PREFIX+token;
                //获取缓存中的数据
                UserInfo userInfo = (UserInfo) this.redisTemplate.opsForValue().get(userLoginKey);
                //判断用户对象
                if(null != userInfo){
                    //将用户id存储到本地
                    AuthContextHolder.setUserId(userInfo.getId());
                }
            }
            //  声明一个对象
            Object proceed;
            try {
                //  执行业务方法
                proceed = point.proceed();
            } finally {
                //  防止内存泄漏;
                AuthContextHolder.removeUserId();
            }

            return proceed;
        }
    }

